package at.ac.tuwien.vpsbcm.spielzeugfabrik.space;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.mozartspaces.capi3.CountNotMetException;
import org.mozartspaces.capi3.FifoCoordinator;
import org.mozartspaces.capi3.LindaCoordinator;
import org.mozartspaces.capi3.Selector;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.DefaultMzsCore;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsConstants;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.notifications.Notification;
import org.mozartspaces.notifications.NotificationListener;
import org.mozartspaces.notifications.NotificationManager;
import org.mozartspaces.notifications.Operation;

/**
 * @author Andreas Wolf
 *
 */

public class Logistiker implements Runnable, NotificationListener {
	private volatile boolean goOn;
	private volatile boolean checkForNew;
	private Integer ID;
	private URI spaceurl;
	private DefaultMzsCore core;
	private Capi capi;
	private NotificationManager notifManager;
	
	private ContainerReference LokomotivenContainer;
	private ContainerReference AusgeliefertContainer;
	private final int transDuration = 2000;

	public Logistiker (URI spaceurl, Integer ID) {
		this.ID = ID.intValue();
		this.goOn = true;
		this.checkForNew = true;
		try {
			this.spaceurl = spaceurl;
			core = DefaultMzsCore.newInstanceWithoutSpace();
			capi = new Capi(core);
			LokomotivenContainer = capi.lookupContainer("Lokomotiven", spaceurl, 0, null);
			AusgeliefertContainer = capi.lookupContainer("Ausgeliefert", spaceurl, 0, null);
			notifManager = new NotificationManager(core);
			notifManager.createNotification(LokomotivenContainer, this, Operation.WRITE, Operation.DELETE, Operation.TAKE);
		} catch (MzsCoreException e) {
			this.goOn = false;
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
	
	public void run() {
		TransactionReference tx = null;
		ArrayList<Lokomotive> LokA;
		Lokomotive lok, LokTpl;
		while (this.goOn) {
			try {
				try {
					tx = capi.createTransaction(transDuration, spaceurl);
					System.out.println("Suche fertige Lokomotiven");
					LokTpl = new Lokomotive(null, null, null, true, null, null, null);
					ArrayList<Selector> selectorlist = new ArrayList<Selector>();
					selectorlist.add(LindaCoordinator.newSelector(LokTpl, MzsConstants.Selecting.COUNT_MAX));
					selectorlist.add(FifoCoordinator.newSelector(1));
					
					LokA = capi.take( LokomotivenContainer
							 , selectorlist 
							 , 0
							 , tx
							 ); 
					lok = LokA.get(0);
					lok.setLogistikerID(new Integer(ID));
					capi.write(new Entry(lok, FifoCoordinator.newCoordinationData()), AusgeliefertContainer, 0, tx);
					capi.commitTransaction(tx);
					System.out.println("Fertige Lokomotive gefunden und ausgeliefert");
				} catch (CountNotMetException e) {
					System.out.println("Keine fertigen Lokomotiven gefunden");
					capi.rollbackTransaction(tx);
					waitCheckForNew();
				}
			} catch (MzsCoreException e) {
				e.printStackTrace();
			}
		}
		notifManager.shutdown();
		core.shutdown(false);
	}

	public boolean isRunning () {
		return this.goOn;
	}
	
	public void stop () {
		this.goOn = false;
	}

	private synchronized void waitCheckForNew () {
		while (!this.checkForNew) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
		this.checkForNew = false;
	}
	
	public synchronized void setCheckForNew () {
		this.checkForNew = true;
		notifyAll();
	}
	
	public void entryOperationFinished(Notification source,
			Operation operation, List<? extends Serializable> entries) {
		setCheckForNew();
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		URI spaceurl;
		Logistiker logi;
		int ID, c;
		BufferedReader br = new BufferedReader (new InputStreamReader(System.in));
		if (args.length == 2) {
			try {
				spaceurl = new URI(args[0]);
				ID = Integer.parseInt(args[1]);
				logi = new Logistiker(spaceurl, ID);					
				Thread t = new Thread(logi);
				System.out.println("Starte Logistiker - Abbruch mit der Taste 'q'");
	            t.start();
	            c = br.read();
	            while (c != 'q' && c != 'Q' && logi.isRunning()) {
	            	c = br.read();
	            }
	            logi.stop();
	            logi.setCheckForNew();
			} catch (URISyntaxException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		} else {
			System.out.println("Brauche Space-URI und LogistikerID als Kommandozeilenparameter!");		
		}

	}
}
